package gl.drawer

import gl.util.GLUtils
import org.lwjgl.glfw.GLFW
import org.lwjgl.opengl.GL44C.*
import org.lwjgl.system.MemoryStack

class Tester: Drawer() {

    private val vertices = floatArrayOf(
        // 左下
        -0.75f, -0.75f, -0.25f, -0.75f, -0.25f, -0.25f, -0.75f, -0.25f,
        // 右下
        0.25f, -0.75f, 0.75f, -0.75f, 0.75f, -0.25f, 0.25f, -0.25f,
        // 左上
        -0.75f, 0.25f, -0.25f, 0.25f, -0.25f, 0.75f, -0.75f, 0.75f,
        // 右上
        0.25f, 0.25f, 0.75f, 0.25f, 0.75f, 0.75f, 0.25f, 0.75f,
        // uv
        0f, 0f, 1f, 0f, 1f, 1f, 0f, 1f,
        // uv 用于 GL_TEXTURE_WRAP_S GL_TEXTURE_WRAP_T
        0f, 0f, 2f, 0f, 2f, 2f, 0f, 2f,
    )
    private val textures = byteArrayOf(
        0xFF.toByte(), 0x00, 0xFF.toByte(), 0x00, 0xFF.toByte(), 0x00, 0xFF.toByte(), 0x00,
        0x00, 0xFF.toByte(), 0x00, 0xFF.toByte(), 0x00, 0xFF.toByte(), 0x00, 0xFF.toByte(),
        0xFF.toByte(), 0x00, 0xFF.toByte(), 0x00, 0xFF.toByte(), 0x00, 0xFF.toByte(), 0x00,
        0x00, 0xFF.toByte(), 0x00, 0xFF.toByte(), 0x00, 0xFF.toByte(), 0x00, 0xFF.toByte(),
        0xFF.toByte(), 0x00, 0xFF.toByte(), 0x00, 0xFF.toByte(), 0x00, 0xFF.toByte(), 0x00,
        0x00, 0xFF.toByte(), 0x00, 0xFF.toByte(), 0x00, 0xFF.toByte(), 0x00, 0xFF.toByte(),
        0xFF.toByte(), 0x00, 0xFF.toByte(), 0x00, 0xFF.toByte(), 0x00, 0xFF.toByte(), 0x00,
        0x00, 0xFF.toByte(), 0x00, 0xFF.toByte(), 0x00, 0xFF.toByte(), 0x00, 0xFF.toByte(),
    )

    private var program = 0
    private var vao = 0
    private var texture = 0
    private val buffers = IntArray(2)

    override fun initialize() {
        // shader
        val sv = glCreateShader(GL_VERTEX_SHADER)
        glShaderSource(sv, vSrc)
        glCompileShader(sv)
        val fv = glCreateShader(GL_FRAGMENT_SHADER)
        glShaderSource(fv, fSrc)
        glCompileShader(fv)
        program = glCreateProgram()
        glAttachShader(program, sv)
        glAttachShader(program, fv)
        glLinkProgram(program)
        glDetachShader(program, sv)
        glDetachShader(program, fv)
        glDeleteShader(sv)
        glDeleteShader(fv)
        glUseProgram(program)
        // buffer
        glGenBuffers(buffers)
        glBindBuffer(GL_ARRAY_BUFFER, buffers[0])
        glBufferStorage(GL_ARRAY_BUFFER, vertices, 0)
        glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffers[1])
        glBufferStorage(GL_PIXEL_UNPACK_BUFFER, MemoryStack.stackGet().bytes(*textures), 0)
        // attribute
        vao = glGenVertexArrays()
        glBindVertexArray(vao)
        glEnableVertexAttribArray(0)
        glEnableVertexAttribArray(1)
        // image
        texture = glGenTextures()
        glActiveTexture(GL_TEXTURE0)
        glBindTexture(GL_TEXTURE_2D, texture)
        glTexStorage2D(GL_TEXTURE_2D, 1, GL_R8, 8, 8)
        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 8, 8, GL_RED, GL_UNSIGNED_BYTE, 0)
        glUniform1i(glGetUniformLocation(program, "tex"), 0)
    }

    override fun update(): Boolean {
        glClear(GL_COLOR_BUFFER_BIT)

        glTexParameterIi(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
        glVertexAttribPointer(0, 2, GL_FLOAT, false, 0, 0)
        glVertexAttribPointer(1, 2, GL_FLOAT, false, 0, 128)
        glDrawArrays(GL_TRIANGLE_FAN, 0, 4)

        glTexParameterIi(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
        glVertexAttribPointer(0, 2, GL_FLOAT, false, 0, 32)
        glVertexAttribPointer(1, 2, GL_FLOAT, false, 0, 128)
        glDrawArrays(GL_TRIANGLE_FAN, 0, 4)

        glTexParameterIi(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
        glTexParameterIi(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
        glVertexAttribPointer(0, 2, GL_FLOAT, false, 0, 64)
        glVertexAttribPointer(1, 2, GL_FLOAT, false, 0, 160)
        glDrawArrays(GL_TRIANGLE_FAN, 0, 4)

        glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, floatArrayOf(1f, 0f, 0f, 1f))
        glTexParameterIi(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT)
        glTexParameterIi(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER)
        glVertexAttribPointer(0, 2, GL_FLOAT, false, 0, 96)
        glVertexAttribPointer(1, 2, GL_FLOAT, false, 0, 160)
        glDrawArrays(GL_TRIANGLE_FAN, 0, 4)
        GLUtils.capture()
        return false
    }

    override fun terminal() {
        glBindVertexArray(0)
        glBindBuffer(GL_ARRAY_BUFFER, 0)
        glUseProgram(0)
        glDeleteVertexArrays(vao)
        glDeleteProgram(program)
        glDeleteBuffers(buffers)
        glBindTexture(GL_TEXTURE_2D, 0)
        glDeleteTextures(texture)
    }

    override fun stop() {
    }

    override fun resume() {
        super.resume()
    }

    override fun handleKey(keyCode: Int, action: Int) {
        if (action == GLFW.GLFW_PRESS && keyCode == GLFW.GLFW_KEY_R) {
            stop()
            terminal()
            initialize()
            resume()
        }
    }

    override fun onResize(width: Int, height: Int) {
    }
}

private val vSrc = """
#version 400 core

layout(location=0) in vec2 vPosition;
layout(location=1) in vec2 vTexCoord;

out vec2 texCoord;

void main() {
    gl_Position = vec4(vPosition, 0.0, 1.0);
    texCoord = vTexCoord;
}
""".trimIndent()

private val fSrc = """
#version 400 core

uniform sampler2D tex;

in vec2 texCoord;

out vec4 fColor;

void main() {
    fColor = texture(tex, texCoord);
}
""".trimIndent()
